﻿using System;
using Autofac;
using CSharpLogger.Common;

namespace CSharpLogger
{
    /// <summary>    
    /// Implements <see cref="ILoggerHandler"/> as file logger
    /// </summary>
    /// <remarks>Factory Method: Concrete Product</remarks>
    public class FileLogger : ILoggerHandler
    {
        /// <summary>
        /// Lock object for writing to the text file
        /// </summary>
        private static object _current = new object();

        /// <summary>
        /// Singleton Pattern. We want to create just one instance of this class
        /// </summary>
        private static volatile FileLogger logger;

        /// <summary>
        /// Lock object for accesing this instance
        /// </summary>
        private static object singleton = new object();

        /// <summary>
        /// Custom <see cref="IFileWrapper"/> requiered for UnitTesting
        /// /// </summary>
        public IFileWrapper FileWrapper;

        /// <summary>
        /// Singleton instance of <see cref="FileLogger"/>
        /// </summary>
        /// <remarks>https://msdn.microsoft.com/en-us/library/ff650316.aspx</remarks>
        public static FileLogger Logger
        {
            get
            {
                lock (singleton)
                {
                    if (logger == null)
                        logger = new FileLogger();

                    return logger;
                }
            }
        }

        /// <summary>
        /// Gets or sets the file name where the log will be stored
        /// </summary>
        public string FileName { get; set; }

        /// <summary>
        /// Gets or sets the path where the <para>FileName</para> will be stored
        /// </summary>
        public string Path { get; set; }

        /// <summary>
        /// Creates an instance of type <see cref="FileLogger"/>
        /// </summary>
        /// <remarks>Implements the Singleton Pattern</remarks>
        private FileLogger()
        {
            FileName = string.Format("\\{0: MMM dd, yy}.log", DateTime.Now);
            Path = Environment.CurrentDirectory;

            FileWrapper = ContainerBuilderFactory.Container.Resolve<IFileWrapper>();
        }

        /// <summary>
        /// Log the <paramref name="Message"/> in the <paramref name="Path"/> specified.
        /// The <paramref name="UserName"/>, <paramref name="Host"/> must be supplied
        /// </summary>
        /// <example>
        ///     <code>
        ///         var handler = new LoggerHandlerFactory();
        ///         var logger = handler.GetHandler<FileLogger>();
        ///         logger.Log("Hello CSharpLogger");
        ///     </code>
        /// </example>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="FileNotFoundException"></exception>
        /// <exception cref="IOException"></exception>
        /// <exception cref="SecurityException"></exception>
        /// <exception cref="DirectoryNotFoundException"></exception>
        /// <exception cref="UnauthorizedAccessException"></exception>
        /// <exception cref="PathTooLongException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="FormatException"></exception>
        public void Log(string message, LogLevel level = LogLevel.INFO)
        {
            lock (_current)
            {
                var configLevel = CSharpLoggerConfiguration.Configuration.GetLogLevel();

                if (configLevel != LogLevel.OFF & level != LogLevel.OFF && configLevel >= level)
                {
                    try
                    {
                        FileWrapper.WriteLog(string.Concat(Path, FileName), message, level);
                    }
                    catch (CSharpLoggerException)
                    {
                        throw;
                    }
                }
            }
        }
    }
}